#include <iostream>
#include <stdexcept>
#include <functional>
#include <thread>

#include "bglApplication.h"

static BGLApplication* gThis;

void __FramebufferSizeCallback(GLFWwindow* window, int width, int height)
{
    std::function<void(GLFWwindow*, int, int)> __frameBuffer = std::bind(&BGLApplication::FramebufferSizeCallback, gThis, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    __frameBuffer(window, width, height);
}

void __ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
    std::function<void(GLFWwindow*, double, double)> __scroll = std::bind(&BGLApplication::ScrollCallback, gThis, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    __scroll(window, xoffset, yoffset);
}

void __CursorPositionCallback(GLFWwindow * window, double xpos, double ypos)
{
    std::function<void(GLFWwindow*, double, double)> __cursorPosition = std::bind(&BGLApplication::CursorPositionCallback, gThis, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    __cursorPosition(window, xpos, ypos);
}

void __Mousebuttoncallback(GLFWwindow* window, int button, int action, int mods)
{
    std::function<void(GLFWwindow*, int, int, int)> __mousebutton = std::bind(&BGLApplication::MousebuttonCallback, gThis, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
    __mousebutton(window, button, action, mods);
}

void __KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    std::function<void(GLFWwindow*, int, int, int, int)> __key = std::bind(&BGLApplication::KeyCallback, gThis, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5);
    __key(window, key, scancode, action, mods);
}

BGLApplication::BGLApplication()
{
    gThis = this;
}

BGLApplication::~BGLApplication()
{

}

void BGLApplication::PushLayer(BGLLayer* layer)
{
    m_layerStack.PushLayer(layer);
}
void BGLApplication::PushOverlay(BGLLayer* layer)
{
    m_layerStack.PushOverlay(layer);
}

void BGLApplication::__init()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    m_window = glfwCreateWindow(600, 400, "LearnOpenGL", NULL, NULL);
    if (m_window == nullptr)
    {
        glfwTerminate();
        throw std::runtime_error("create window failed");
    }

    glfwMakeContextCurrent(m_window);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
       throw std::runtime_error("gladLoadGLLoader failed");
    }

    this->__setCallback();

    this->Init();
}

void BGLApplication::__render()
{
    while (!glfwWindowShouldClose(m_window)) {

        this->Render();

        for (BGLLayer* layer : m_layerStack)
				layer->OnUpdate();
        
        glfwSwapBuffers(m_window);
		glfwPollEvents();
	}

	glfwDestroyWindow(m_window);

	glfwTerminate(); 
}

void BGLApplication::Run()
{
    this->__init();
    this->__render();
}

void BGLApplication::GetWindowSize(int* width, int* height)
{
    glfwGetWindowSize(m_window, width, height);
}

void BGLApplication::GetWindowPos (int* xPos, int* yPos)
{
    glfwGetWindowPos(m_window, xPos, yPos);
}

void BGLApplication::SetWindowSize(int width, int height)
{
    glfwSetWindowSize(m_window, width, height);
}

void BGLApplication::SetWindowPos (int xPos, int yPos)
{
    glfwSetWindowSize(m_window, xPos, yPos);
}

void BGLApplication::FramebufferSizeCallback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

void BGLApplication::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset)
{
   //std::cout << "xoffset: " << xoffset << "    " << "yoffset: " << yoffset << std::endl; 
}

void BGLApplication::CursorPositionCallback(GLFWwindow * window, double xpos, double ypos)
{
    //std::cout << "xpos: " << xpos << "    " << "ypos: " << ypos << std::endl; 
}

void BGLApplication::MousebuttonCallback(GLFWwindow* window, int button, int action, int mods)
{
    // if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS)
    // {
    //     std::cout << "press right button" << std::endl;
    // }  
}

void BGLApplication::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    // if (key == GLFW_KEY_E && action == GLFW_PRESS)
    // if (action == GLFW_PRESS)
    // {
    //     std::cout << "press key: " << key << std::endl;
    // } 
}

void BGLApplication::__setCallback()
{
    glfwSetFramebufferSizeCallback(m_window, __FramebufferSizeCallback);
    glfwSetScrollCallback(m_window, __ScrollCallback);
    glfwSetCursorPosCallback(m_window, __CursorPositionCallback);
    glfwSetMouseButtonCallback(m_window, __Mousebuttoncallback);
    glfwSetKeyCallback(m_window, __KeyCallback);
}

